//-------------------------------------------------------------------
//	Copyright (c) 2012-2014 Zhirnov Andrey
//	This file is part of the "UXGEN-ES" project.
//	See copyright notice in "Engine.h".
//-------------------------------------------------------------------

#include "Utils/Texture/Sprite/SpriteConverter.h"
#include "Shared/Resources/ResourceFileInfo.h"

namespace Engine
{
namespace Utils
{
	
	CMetaClass	SpriteConverter::metaSpriteAnim;
	CMetaClass	SpriteConverter::metaSprite;
	CMetaClass	SpriteConverter::metaSpriteList;

}
	
	UX_CREATE_META_OBJECT(	Utils::SpriteConverter::MetaSpriteAnim,		Utils::SpriteConverter::metaSpriteAnim );
	UX_CREATE_META_OBJECT(	Utils::SpriteConverter::MetaSprite,			Utils::SpriteConverter::metaSprite );
	UX_CREATE_META_OBJECT(	Utils::SpriteConverter::MetaSpriteList,		Utils::SpriteConverter::metaSpriteList );
	UX_ENUM_META_OBJECT(	Utils::EPixelFormat::type );


namespace Utils
{

	// InitMetaData
	void SpriteConverter::InitMetaData ()
	{
		CMetaAddIteratorPtr	iter;

		CMetaClass::s_InitBaseTypes();

		if ( not metaSpriteAnim.IsInit() )
		{
			MetaSpriteAnim	obj;

			metaSpriteAnim.CreateExt( "SpriteAnim", obj );
			iter = metaSpriteAnim.GetAddIter( obj );

			iter->AddField( obj.blend,		"blend"	 );
			iter->AddField( obj.frames,		"frames" );
			iter->AddField( obj.rate,		"rate"	 );
			iter->AddField( obj.type,		"type"	 );
		}

		if ( not metaSprite.IsInit() )
		{
			MetaSprite	obj;

			metaSprite.CreateExt( "Sprite2D", obj );
			iter = metaSprite.GetAddIter( obj );
		
			MetaData::MetaDataUtils::InitMetaData( obj.physics );
			MetaData::MetaDataUtils::InitMetaData( obj.material );

			iter->AddField( obj.animations,		"animations"	);
			iter->AddField( obj.frameMargin,	"frame_margin"	);
			iter->AddField( obj.frameSize,		"frame_size"	);
			iter->AddField( obj.image,			"image"			);
			iter->AddField( obj.material,		"material"		);
			iter->AddField( obj.newFormat,		"new_format"	);
			iter->AddField( obj.physics,		"physics"		);
			iter->AddField( obj.color,			"color"			);
		}

		if ( not metaSpriteList.IsInit() )
		{
			MetaSpriteList	obj;

			metaSpriteList.CreateExt( "SpriteList", obj );
			iter = metaSpriteList.GetAddIter( obj );

			iter->AddField( obj.extension,	"extension"	 );
			iter->AddField( obj.inputDir,	"input_dir"	 );
			iter->AddField( obj.outputDir,	"output_dir" );
			iter->AddField( obj.sprites,	"sprites"	 );
		}
	}


	// LoadConfig
	bool SpriteConverter::LoadConfig (uni_c_string filename, uni_c_string outputPath, bool clearOutputDir)
	{
		static_obj_tree_t	cfg_tree;
		MetaSpriteList		sprite_list;

		cfg_tree.Push( TConfigObjInfo( metaSpriteList.GetSetIter( sprite_list ), null ) );

		string	input_path = FileAddressUtils::GetPath( filename );

		_core->Config()->ClearDerectories();
		_core->Config()->AddDirectory( input_path );

		CHECK_ERR( _core->Parse( filename, cfg_tree ) );


		string	input_dir;
		string	output_dir;

		FileAddressUtils::BuildPath( input_dir, input_path, sprite_list.inputDir );
		FileAddressUtils::BuildPath( output_dir, outputPath, sprite_list.outputDir );
		
		// create output dir
		if ( not FileSystem::IsDirectoryExist( output_dir.cstr() ) )
		{
			FileSystem::NewDirectory( output_dir.cstr() );
		}

		if ( clearOutputDir )
		{
			_core->ClearDir( output_dir );
		}
		

		_core->Config()->ClearDerectories();
		_core->Config()->AddDirectory( input_dir );


		FOR( i, sprite_list.sprites )
		{
			string	in_name;
			FileAddressUtils::BuildPath( in_name, input_dir, sprite_list.sprites[i] );

			const string	name = FileAddressUtils::GetName( sprite_list.sprites[i] );

			string	out_name;
			FileAddressUtils::BuildPath( out_name, output_dir, name, sprite_list.extension );

			MetaData::Sprite2dCfg	sprite;
			Image					image;
			bool					res;

			CHECK( res = _LoadSprite( in_name, input_dir, sprite, image ) );

			if ( not res )
				continue;

			CHECK( res = _SaveSprite( out_name, output_dir, sprite, image ) );
		}

		return true;
	}
	

	// _LoadSprite
	bool SpriteConverter::_LoadSprite (uni_c_string filename, uni_c_string inputDir,
										MetaData::Sprite2dCfg &sprite, Image &image)
	{
		static_obj_tree_t	cfg_tree;
		MetaSprite			sprite_cfg;

		cfg_tree.Push( TConfigObjInfo( metaSprite.GetSetIter( sprite_cfg ), null ) );
		
		CHECK_ERR( _core->Parse( filename, cfg_tree ) );


		string	img_name;
		FileAddressUtils::BuildPath( img_name, inputDir, sprite_cfg.image );
		
		CHECK_ERR( ImageLoader::Load( img_name, image, 1.0f, false ) );
		CHECK_ERR( ImageConverter::Convert( image, (EPixelFormat::type) sprite_cfg.newFormat ) );


		const vec2		img_size	 = image.Dimension().xy().To<vec2>();
		const vec2		frame_margin = sprite_cfg.frameMargin.To<vec2>() / img_size;

		sprite.material	= sprite_cfg.material;
		sprite.color	= sprite_cfg.color;
		

		// frames
		const uvec2	num_frames	= ( image.Dimension().xy() / sprite_cfg.frameSize );
		const uint	count		= num_frames.x * num_frames.y;

		sprite.texcoords.ClearMem();

		for (uint i = 0; i < count; ++i)
		{
			rect	r;
			UXMathExt::GetTextureFrame( r, i, image.Dimension().xy(), num_frames );

			r.LeftBottom() += frame_margin;
			r.RightTop()   -= frame_margin;

			sprite.texcoords.PushBack( r );
		}


		// animations
		FOR( i, sprite_cfg.animations )
		{
			MetaSpriteAnim const &			a = sprite_cfg.animations[i];
			MetaData::Sprite2dAnimationCfg	anim;

			ASSERT( IsNotZero( a.rate ) );

			anim.blend				= a.blend;
			anim.rate				= 1.0f / a.rate;
			anim.frameInterval.x	= sprite.frames.Count();

			FOR( j, a.frames )
			{
				if ( a.frames[j] >= count )
				{
					WARNING( "frame index in out of range" );
					continue;
				}
				sprite.frames.PushBack( a.frames[j] );
			}

			anim.frameInterval.y	= sprite.frames.LastIndex();

			sprite.animations.Add( a.type, anim );
		}

		return true;
	}


	// _SaveSprite
	bool SpriteConverter::_SaveSprite (uni_c_string filename, uni_c_string outputDir,
										const MetaData::Sprite2dCfg &sprite, Image &image)
	{
		WFilePtr	file;
		CHECK_ERR( (file = File::HddWFile::New( filename )).IsNotNull() );

		RESFILE_WRITE_HEADER( file, SpriteFileInfo );

		CHECK_ERR( sprite.Serialize( file ) );

		CHECK_ERR( ImageSaver::Save( file, image ) );
		return true;
	}


}	// Utils
}	// Engine